package ccm.tools;

import ccm.common.CCMConfigManager;
import ccm.component.override.JButton;
import ccm.component.override.JCheckBox;
import ccm.component.override.JComboBox;
import ccm.component.override.JFrame;
import ccm.component.override.JLabel;
import ccm.component.override.JSpinner;
import ccm.component.override.JTextField;
import ccm.component.override.*;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;


public class GenerateFont extends JFrame implements ActionListener
{
    private static final String[] title=new String[]{
            "tools",
            "GenerateFont"
    };

    static
    {
        CCMConfigManager.setStringIfAbsent("ccm/tools/GenerateFont/lastDir","");
    }

    protected final JButton addJButton;
    protected final JCheckBox sameFileJCheckBox;
    protected final JButton generateJButton;
    protected final JButton resetJButton;
    protected final JPanel fontJPanes;

    public GenerateFont()
    {
        super(title);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        JPanel configJPanel=new JPanel();
        add(configJPanel,BorderLayout.NORTH);
        configJPanel.setLayout(new FlowLayout(FlowLayout.LEFT));

        addJButton=new JButton("添加");
        configJPanel.add(addJButton);
        addJButton.addActionListener(this);

        sameFileJCheckBox=new JCheckBox("生成到同一文件",true);
        configJPanel.add(sameFileJCheckBox);
        sameFileJCheckBox.addActionListener(this);
        sameFileJCheckBox.setSelected(true);

        generateJButton=new JButton("生成");
        configJPanel.add(generateJButton);
        generateJButton.addActionListener(this);

        resetJButton=new JButton("重置");
        configJPanel.add(resetJButton);
        resetJButton.addActionListener(this);

        fontJPanes=new JPanel();
        this.add(fontJPanes,BorderLayout.CENTER);
        fontJPanes.setLayout(new VFlowLayout());
        {
            int cnt=CCMConfigManager.setIntegerIfAbsent("ccm/tools/GenerateFont/count",0);
            for(int i=0;i<cnt;++i){fontJPanes.add(new FontJPanel(new FontJPanelConfig(i)));}
        }
        if(fontJPanes.getComponentCount()==0){resetFontJPanel();}
        setVisible(true);
    }

    private void resetFontJPanel()
    {
        fontJPanes.removeAll();

        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",8,5,8,0,-1,true,true,true)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",10,6,10,0,-1,true,true,true)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",12,6,12,0,-2,true,true,true)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",16,8,16,0,-2,true,true,true)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",24,12,24,0,-3,true,true,true)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("宋体","SimSun",32,16,32,0,-4,true,true,true)));

        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",8,4,12,0,-1,true,true,false)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",10,6,12,0,-1,true,true,false)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",12,7,12,0,-2,true,true,false)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",16,9,16,0,-2,true,true,false)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",24,13,24,0,-3,true,true,false)));
        fontJPanes.add(new FontJPanel(new FontJPanelConfig("Consolas","Consolas",32,18,32,0,-4,true,true,false)));


        fontJPanes.updateUI();
        fontJPanes.invalidate();
        fontJPanes.validate();
        fontJPanes.repaint();
        writeConfig();
    }

    public void writeConfig()
    {
        System.out.println("writeConfig "+fontJPanes.getComponentCount());
        int cnt=0;
        for(Component com: fontJPanes.getComponents())
        {
            if(com instanceof FontJPanel)
            {
                (((FontJPanel)com).getConfig()).writeExternal(cnt);
                ++cnt;
            }
        }
        System.out.println(cnt);
        CCMConfigManager.setInteger("ccm/tools/GenerateFont/count",cnt);
    }

    public static void main(String[] args)
    {
        new GenerateFont();
    }

    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource().equals(addJButton))
        {
            fontJPanes.add(new FontJPanel());
            fontJPanes.updateUI();
            fontJPanes.invalidate();
            fontJPanes.validate();
            fontJPanes.repaint();
        }
        else if(e.getSource().equals(resetJButton))
        {
            resetJButton.setEnabled(false);
            resetFontJPanel();
            resetJButton.setEnabled(true);
        }
        else if(e.getSource().equals(generateJButton))
        {
            generateJButton.setEnabled(false);
            try
            {
                BufferedWriter outC=new BufferedWriter(new FileWriter("ScnsLcd.table.c"));
                BufferedWriter outH=new BufferedWriter(new FileWriter("ScnsLcd.table.h"));
                BufferedWriter other=new BufferedWriter(new FileWriter("other.c"));

                outC.write("#include \"ScnsLcd.c.h\"\n");
                outC.write("#if defined(SCNS_LCD_ENABLE)&&SCNS_LCD_ENABLE==1\n");

                outH.write("#ifndef __SCNS_LCD_TABLE_H__\n#define __SCNS_LCD_TABLE_H__\n#include \"ScnsLcd.h\"\n#if defined(SCNS_LCD_ENABLE)&&SCNS_LCD_ENABLE==1\n");

                for(Component com: fontJPanes.getComponents())
                {
                    if(com instanceof FontJPanel)
                    {((FontJPanel)com).generate(outC,outH,other);}
                }

                outC.write("#endif\n");
                outH.write("#endif\n#endif\n");

                outC.close();
                outH.close();
                other.close();

                JOptionPane.showMessageDialog(this,"已生成","已生成",JOptionPane.PLAIN_MESSAGE);
                generateJButton.setText("生成");
            }catch(IOException ex)
            {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(this,"IO错误","生成失败",JOptionPane.ERROR_MESSAGE);
            }
            generateJButton.setEnabled(true);

            // JFileChooser jFileChooser=new JFileChooser("");
            // jFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            // jFileChooser.showDialog(new JLabel(),"选择");
            // smctp.receiveData(new ReceiveDataEvent(this,FileSaver.readAll(jFileChooser.getSelectedFile())));
        }
    }

    static class FontJPanelConfig
    {
        boolean all;
        boolean ascii;
        boolean gbk;
        String text;
        String outName;
        String fontType;
        int fontHeight;
        int fontWidthAscii;
        int fontWidthGbk;
        int xBias;
        int yBias;

        public FontJPanelConfig()
        {
            this("","",1,1,1,0,0,true,true,true,"");
        }

        public FontJPanelConfig(String fontType,String outName,int fontHeight,int fontWidthAscii,int fontWidthGbk,int xBias,int yBias,boolean all,boolean ascii,boolean gbk,String text)
        {
            this.all=all;
            this.ascii=ascii;
            this.gbk=gbk;
            this.text=text;
            this.outName=outName;
            this.fontType=fontType;
            this.fontHeight=fontHeight;
            this.fontWidthAscii=fontWidthAscii;
            this.fontWidthGbk=fontWidthGbk;
            this.xBias=xBias;
            this.yBias=yBias;
        }

        public FontJPanelConfig(int i)
        {
            this("","",1,1,1,0,0,true,true,true,"");
            readExternal(i);
        }

        public void readExternal(int i)
        {
            all=CCMConfigManager.getBoolean("ccm/tools/GenerateFont/font"+i+"/all");
            ascii=CCMConfigManager.getBoolean("ccm/tools/GenerateFont/font"+i+"/ascii");
            gbk=CCMConfigManager.getBoolean("ccm/tools/GenerateFont/font"+i+"/gbk");
            text=CCMConfigManager.getString("ccm/tools/GenerateFont/font"+i+"/text");
            outName=CCMConfigManager.getString("ccm/tools/GenerateFont/font"+i+"/outName");
            fontType=CCMConfigManager.getString("ccm/tools/GenerateFont/font"+i+"/fontType");
            fontHeight=CCMConfigManager.getInteger("ccm/tools/GenerateFont/font"+i+"/fontHeight");
            fontWidthAscii=CCMConfigManager.getInteger("ccm/tools/GenerateFont/font"+i+"/fontWidthAscii");
            fontWidthGbk=CCMConfigManager.getInteger("ccm/tools/GenerateFont/font"+i+"/fontWidthGbk");
            xBias=CCMConfigManager.getInteger("ccm/tools/GenerateFont/font"+i+"/xBias");
            yBias=CCMConfigManager.getInteger("ccm/tools/GenerateFont/font"+i+"/yBias");
        }

        public FontJPanelConfig(String fontType,String outName,int size,int xBias,int yBias)
        {
            this(fontType,outName,size,size/2,size,xBias,yBias,true,true,true,"");
        }

        public FontJPanelConfig(String fontType,String outName,int fontHeight,int fontWidthAscii,int fontWidthGbk,int xBias,int yBias,boolean all,boolean ascii,boolean gbk)
        {
            this(fontType,outName,fontHeight,fontWidthAscii,fontWidthGbk,xBias,yBias,all,ascii,gbk,"");
        }

        public void writeExternal(int i)
        {
            CCMConfigManager.setBoolean("ccm/tools/GenerateFont/font"+i+"/all",all);
            CCMConfigManager.setBoolean("ccm/tools/GenerateFont/font"+i+"/ascii",ascii);
            CCMConfigManager.setBoolean("ccm/tools/GenerateFont/font"+i+"/gbk",gbk);
            CCMConfigManager.setString("ccm/tools/GenerateFont/font"+i+"/text",text);
            CCMConfigManager.setString("ccm/tools/GenerateFont/font"+i+"/outName",outName);
            CCMConfigManager.setString("ccm/tools/GenerateFont/font"+i+"/fontType",fontType);
            CCMConfigManager.setInteger("ccm/tools/GenerateFont/font"+i+"/fontHeight",fontHeight);
            CCMConfigManager.setInteger("ccm/tools/GenerateFont/font"+i+"/fontWidthAscii",fontWidthAscii);
            CCMConfigManager.setInteger("ccm/tools/GenerateFont/font"+i+"/fontWidthGbk",fontWidthGbk);
            CCMConfigManager.setInteger("ccm/tools/GenerateFont/font"+i+"/xBias",xBias);
            CCMConfigManager.setInteger("ccm/tools/GenerateFont/font"+i+"/yBias",yBias);
        }
    }

    private class FontJPanel extends JPanel implements ActionListener, ChangeListener
    {
        protected final JButton deleteJButton;
        protected final JCheckBox allJCheckBox;
        protected final JCheckBox asciiJCheckBox;
        protected final JCheckBox gbkJCheckBox;
        protected final JTextField textJTextField;
        protected final JTextField outNameJTextField;
        protected final JPanel viewJPanel;
        protected final JSpinner fontHeightJSpinner;
        protected final JSpinner fontWidthAsciiJSpinner;
        protected final JSpinner fontWidthGbkJSpinner;
        protected final JSpinner xBiasJSpinner;
        protected final JSpinner yBiasJSpinner;
        protected final JComboBox<String> fontTypeJComboBox;
        private int cnt;


        public FontJPanel()
        {
            super();
            setLayout(new GridLayout(2,1));

            JPanel jPanel=new JPanel();
            add(jPanel);
            jPanel.setLayout(new FlowLayout(FlowLayout.LEFT));

            jPanel.add(new JLabel("字体:"));
            fontTypeJComboBox=new JComboBox<String>(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
            jPanel.add(fontTypeJComboBox);
            fontTypeJComboBox.addActionListener(this);

            jPanel.add(new JLabel("名字:"));
            outNameJTextField=new JTextField("",8);
            jPanel.add(outNameJTextField);
            outNameJTextField.addActionListener(this);

            jPanel.add(new JLabel("字体高度:"));
            fontHeightJSpinner=new JSpinner(new SpinnerNumberModel(1,1,64,1));
            jPanel.add(fontHeightJSpinner);
            fontHeightJSpinner.addChangeListener(this);

            jPanel.add(new JLabel("ASCII宽度:"));
            fontWidthAsciiJSpinner=new JSpinner(new SpinnerNumberModel(1,1,64,1));
            jPanel.add(fontWidthAsciiJSpinner);
            fontWidthAsciiJSpinner.addChangeListener(this);

            jPanel.add(new JLabel("gbk宽度:"));
            fontWidthGbkJSpinner=new JSpinner(new SpinnerNumberModel(1,1,64,1));
            jPanel.add(fontWidthGbkJSpinner);
            fontWidthGbkJSpinner.addChangeListener(this);

            jPanel.add(new JLabel("左右偏移:"));
            xBiasJSpinner=new JSpinner(new SpinnerNumberModel(0,-64,64,1));
            jPanel.add(xBiasJSpinner);
            xBiasJSpinner.addChangeListener(this);

            jPanel.add(new JLabel("上下偏移:"));
            yBiasJSpinner=new JSpinner(new SpinnerNumberModel(0,-64,64,1));
            jPanel.add(yBiasJSpinner);
            yBiasJSpinner.addChangeListener(this);

            allJCheckBox=new JCheckBox("生成全部字体",true);
            jPanel.add(allJCheckBox);
            allJCheckBox.addActionListener(this);
            allJCheckBox.setSelected(true);

            asciiJCheckBox=new JCheckBox("包含ASCII",true);
            jPanel.add(asciiJCheckBox);
            asciiJCheckBox.addActionListener(this);
            asciiJCheckBox.setSelected(true);

            gbkJCheckBox=new JCheckBox("包含GBK",true);
            jPanel.add(gbkJCheckBox);
            gbkJCheckBox.addActionListener(this);
            gbkJCheckBox.setSelected(true);

            textJTextField=new JTextField("火星大王",8);
            jPanel.add(textJTextField);
            textJTextField.setEnabled(false);
            textJTextField.addActionListener(this);


            deleteJButton=new JButton("删除");
            jPanel.add(deleteJButton);
            deleteJButton.addActionListener(this);


            JPanel jPanel2=new JPanel();
            add(jPanel2);
            jPanel2.setLayout(new FlowLayout(FlowLayout.LEFT));
            viewJPanel=new JPanel();
            jPanel2.add(viewJPanel);
            viewJPanel.setLayout(new FlowLayout());
            viewJPanel.setBackground(new Color(0X444444));
            updateViewJPanel();
        }

        public FontJPanel(FontJPanelConfig config)
        {
            this();
            setConfig(config);
            updateViewJPanel();
        }

        @Override
        public void actionPerformed(ActionEvent e)
        {
            if(e.getSource().equals(deleteJButton))
            {
                fontJPanes.remove(this);
                fontJPanes.updateUI();
                fontJPanes.invalidate();
                fontJPanes.validate();
                fontJPanes.repaint();
            }
            textJTextField.setEnabled(!allJCheckBox.isSelected());
            asciiJCheckBox.setEnabled(allJCheckBox.isSelected());
            gbkJCheckBox.setEnabled(allJCheckBox.isSelected());
            fontWidthAsciiJSpinner.setEnabled(asciiJCheckBox.isSelected());
            fontWidthGbkJSpinner.setEnabled(gbkJCheckBox.isSelected());
            fontHeightJSpinner.setEnabled(asciiJCheckBox.isSelected()||gbkJCheckBox.isSelected());
            updateViewJPanel();
            writeConfig();
        }

        private void updateViewJPanel()
        {
            viewJPanel.removeAll();
            final String str=allJCheckBox.isSelected()?((asciiJCheckBox.isSelected()?"HXDW1234_=+!|":"")+(gbkJCheckBox.isSelected()?"火星大王":"")):textJTextField.getText();
            for(int i=0;i<str.length();++i)
            {
                final String word=str.substring(i,i+1);
                viewJPanel.add(new JLabel(new ImageIcon(generateArray(null,word,true))));
            }
            viewJPanel.updateUI();
            viewJPanel.invalidate();
            viewJPanel.validate();
            viewJPanel.repaint();
        }

        private BufferedImage generateArray(BufferedWriter out,String word,boolean newLine)
        {
            if(word.length()!=1)
            {throw new RuntimeException();}
            System.out.println(word);
            final int fontSize=(Integer)fontHeightJSpinner.getValue();
            final int width=(word.getBytes().length==1)?((Integer)fontWidthAsciiJSpinner.getValue()):((Integer)fontWidthGbkJSpinner.getValue());
            final int yBias=(Integer)yBiasJSpinner.getValue();
            final int xBias=(Integer)xBiasJSpinner.getValue();
            final String fontType=(String)fontTypeJComboBox.getSelectedItem();

            BufferedImage image=new BufferedImage(width,fontSize,BufferedImage.TYPE_INT_RGB);
            Graphics2D g=image.createGraphics();
            // try
            // {
            // Font font=Font.createFont(Font.TRUETYPE_FONT,new File("A:\\consola-1.ttf"));
            // font=font.deriveFont(Font.PLAIN,fontSize);
            // FontRenderContext frc=new FontRenderContext(null,false,false);
            // System.out.println(font.getStringBounds(word,frc));
            // System.out.println(fontSize);
            // g.setFont(font);
            // }catch(FontFormatException|IOException e)
            // {
            // throw new RuntimeException(e);
            // }
            g.setFont(new Font(fontType,Font.PLAIN,fontSize));
            g.drawString(word,xBias,fontSize+yBias);

            if(out!=null)
            {
                for(int i=0;i<image.getHeight();++i)
                {
                    for(int j=0, k=0, b=0;j<image.getWidth();++j)
                    {
                        b|=((image.getRGB(j,i)==-1)?1:0)<<k;
                        ++k;
                        if(j==image.getWidth()-1||k==8)
                        {
                            try
                            {
                                out.write(String.format("0X%02X,",b));
                                k=0;
                                b=0;
                                ++cnt;
                                if((cnt%24)==0&&newLine)
                                {out.write("\n    ");}
                            }catch(IOException ex)
                            {
                                ex.printStackTrace();
                            }
                        }
                    }
                }
            }
            return image;
        }

        @Override
        public void stateChanged(ChangeEvent e)
        {
            updateViewJPanel();
            writeConfig();
        }

        private void generate(BufferedWriter outC,BufferedWriter outH,BufferedWriter other) throws IOException
        {
            if(allJCheckBox.isSelected())
            {generate(outC,outH);}
            else
            {generate(other);}
        }

        private void generate(BufferedWriter outC,BufferedWriter outH) throws IOException
        {
            final int fontHeight=(Integer)fontHeightJSpinner.getValue();
            final String name=outNameJTextField.getText();
            outH.write("extern const ScnsLcdFont scnsLcdFont"+name+fontHeight+";\n");

            if(asciiJCheckBox.isSelected())
            {
                outC.write("static const uint8 __scnsLcdFont_ascii_"+name+fontHeight+"[]={\n    ");
                for(int i=' ';i<=(int)'~';++i)
                {generateArray(outC,new String(new byte[]{(byte)i},"GBK"),true);}
                outC.write("\n};\n");
            }
            if(gbkJCheckBox.isSelected())
            {
                outC.write("#if defined(SCNS_LCD_FONT_USE_GBK) && SCNS_LCD_FONT_USE_GBK ==1\n");
                outC.write("static const uint8 __scnsLcdFont_gbk_"+name+fontHeight+"[]={\n    ");
                for(int i=0X81;i<=0XFE;++i)
                {
                    for(int j=0X40;j<=0X7E;++j)
                    {
                        generateArray(outC,new String(new byte[]{
                                (byte)i,
                                (byte)j
                        },"GBK"),true);
                    }
                    for(int j=0X80;j<=0XFE;++j)
                    {
                        generateArray(outC,new String(new byte[]{
                                (byte)i,
                                (byte)j
                        },"GBK"),true);
                    }
                }
                outC.write("\n};\n");
                outC.write("#endif\n");
            }
            outC.write("const ScnsLcdFont scnsLcdFont"+name+fontHeight+"={\n");
            outC.write("    .fontHeight="+fontHeight+",\n");
            outC.write("    .fontAsciiWidth="+fontWidthAsciiJSpinner.getValue()+",\n");
            outC.write("    .name=\""+name+"\",\n");
            if(asciiJCheckBox.isSelected())
            {outC.write("    .ascii=__scnsLcdFont_ascii_"+name+fontHeight+",\n");}
            if(gbkJCheckBox.isSelected())
            {
                outC.write("#if defined(SCNS_LCD_FONT_USE_GBK) && SCNS_LCD_FONT_USE_GBK ==1\n");
                outC.write("    .fontGbkWidth="+fontWidthGbkJSpinner.getValue()+",\n");
                outC.write("    .gbk=__scnsLcdFont_gbk_"+name+fontHeight+",\n");
                outC.write("#endif\n");
            }


            outC.write("};\n");
        }

        private void generate(BufferedWriter other) throws IOException
        {
            final String str=textJTextField.getText();
            if(str.length()!=0)
            {
                final int fontSize=(Integer)fontHeightJSpinner.getValue();
                final String name=outNameJTextField.getText()+fontSize;
                other.write("//"+str+"\n");
                other.write(" static const uint8 "+name+"[]={\n");
                BufferedImage img=null;
                boolean sameSize=true;
                for(int i=0;i<str.length();++i)
                {
                    other.write("    ");
                    final String word=str.substring(i,i+1);
                    final BufferedImage tmp=generateArray(other,word,false);
                    if(img==null)
                    {img=tmp;}
                    sameSize&=(tmp.getHeight()==img.getHeight()&&tmp.getWidth()==img.getWidth());
                    other.write("//"+word+"\n");
                }
                other.write("};\n");
                if(sameSize)
                {other.write("scnsLcdDisplayRawChar(SCNS_LCD_LAYER_0,(Point){0,0},"+name+","+str.length()+","+img.getHeight()+","+img.getWidth()+",SCNS_LCD_COLOR_GREEN,SCNS_LCD_COLOR_BACKGROUND,0);\n");}
            }
        }

        /**
         * 用于保存参数
         */
        public FontJPanelConfig getConfig()
        {
            return new FontJPanelConfig(
                    (String)fontTypeJComboBox.getSelectedItem(),
                    outNameJTextField.getText(),
                    (int)fontHeightJSpinner.getValue(),
                    (int)fontWidthAsciiJSpinner.getValue(),
                    (int)fontWidthGbkJSpinner.getValue(),
                    (int)xBiasJSpinner.getValue(),
                    (int)yBiasJSpinner.getValue(),
                    allJCheckBox.isSelected(),
                    asciiJCheckBox.isSelected(),
                    gbkJCheckBox.isSelected(),
                    textJTextField.getText()
            );
        }

        public void setConfig(FontJPanelConfig config)
        {
            allJCheckBox.setSelected(config.all);
            asciiJCheckBox.setSelected(config.ascii);
            gbkJCheckBox.setSelected(config.gbk);
            textJTextField.setText(config.text);
            outNameJTextField.setText(config.outName);
            fontTypeJComboBox.setSelectedItem(config.fontType);
            fontHeightJSpinner.setValue(config.fontHeight);
            fontWidthAsciiJSpinner.setValue(config.fontWidthAscii);
            fontWidthGbkJSpinner.setValue(config.fontWidthGbk);
            xBiasJSpinner.setValue(config.xBias);
            yBiasJSpinner.setValue(config.yBias);
        }
    }

    @Override
    public void restart()
    {
        dispose();
        main();
    }

    public static void main()
    {
        main(new String[0]);
    }
}

